/*
   Copyright [2020] [lyndon]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
#include <cstdlib>
#include "jni.h"
#include "com_teradata_jni_CptJni.h"
#include "cpt.h"


void print(jni_policy *policy);

/**
 * JNI 加载动态库的时候就会自动调用 JNI_OnLoad 方法
 */
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    jint result = JNI_ERR;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return result;
    }
    register_classes(env); // 注册所有的类
    return JNI_VERSION_1_6;
}

/*
 * Class:     com_teradata_jni_CptJni
 * Method:    encrypt
 * Signature: ([BJB)[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_teradata_jni_CptJni_encrypt
        (JNIEnv *env, jclass obj, jbyteArray instr, jlong key, jbyte cptmode) {
    jbyte *in;
    unsigned char ckey[CLEN];
    int inlen = 0;
    jbyteArray jout;

    in = (*env).GetByteArrayElements(instr, 0);
    inlen = (*env).GetArrayLength(instr);

    jbyte out[inlen];

    init_ckey(key, cptmode, ENCRYPT_KEY, ckey);
    ct_encrypt((unsigned char *) in, inlen, ckey, cptmode, (unsigned char *) out);

    jout = (*env).NewByteArray(inlen);
    (*env).SetByteArrayRegion(jout, 0, inlen, out);
    (*env).ReleaseByteArrayElements(instr, in, 0);

    return jout;
}

/*
 * Class:     com_teradata_jni_CptJni
 * Method:    decrypt
 * Signature: ([BJB)[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_teradata_jni_CptJni_decrypt
        (JNIEnv *env, jclass obj, jbyteArray instr, jlong key, jbyte cptmode) {
    jbyte *in;
    unsigned char ckey[CLEN];
    int inlen = 0;
    jbyteArray jout;

    in = (*env).GetByteArrayElements(instr, 0);

    inlen = (*env).GetArrayLength(instr);
    jbyte out[inlen];

    init_ckey(key, cptmode, DECRYPT_KEY, ckey);
    ct_decrypt((unsigned char *) in, inlen, ckey, cptmode, (unsigned char *) out);

    jout = (*env).NewByteArray(inlen);
    (*env).SetByteArrayRegion(jout, 0, inlen, out);
    (*env).ReleaseByteArrayElements(instr, in, 0);

    return jout;
}

/*
 * Class:     com_teradata_jni_CptJni
 * Method:    multiSubPolicyEncrypt
 * Signature: ([BLcom/teradata/jni/Policy;JB)[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_teradata_jni_CptJni_multiSubPolicyEncrypt
        (JNIEnv *env, jclass jclass1, jbyteArray instr, jobject policyObj, jlong key, jbyte cptmode) {
    jni_policy_t policy;
    policy_java_to_c(env, policyObj, &policy);
    /*printf("sub_policy_num:%d\n", policy.sub_policy_num);
    for(int i = 0; i < policy.sub_policy_num; i++) {
        printf("id: %d position: %d length: %d\n", i, policy.subPolicy[i].position, policy.subPolicy[i].length);
    }*/

    jbyte *in;
    unsigned char ckey[CLEN];
    int inlen = 0;
    jbyteArray jout;

    in = env -> GetByteArrayElements(instr, 0);
    inlen = env -> GetArrayLength(instr);
    outputInstr(reinterpret_cast<const unsigned char *>("encrypt instr"), reinterpret_cast<const unsigned char *>(in));
    jbyte out[inlen];

    init_ckey(key, cptmode, ENCRYPT_KEY, ckey);

    multi_sub_policy_ct_encrypt((unsigned char *) in, &policy, ckey, cptmode, (unsigned char *) out);
    outputInstr(reinterpret_cast<const unsigned char *>("encrypt outstr"), reinterpret_cast<const unsigned char *>(out));
    jout = env -> NewByteArray(inlen);
    env -> SetByteArrayRegion(jout, 0, inlen, out);
    env -> ReleaseByteArrayElements(instr, in, 0);
    return jout;
}

/*
 * Class:     com_teradata_jni_CptJni
 * Method:    multiSubPolicyDecrypt
 * Signature: ([BLcom/teradata/jni/Policy;JB)[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_teradata_jni_CptJni_multiSubPolicyDecrypt
        (JNIEnv *env, jclass jclass1, jbyteArray instr, jobject policyObj, jlong key, jbyte cptmode) {
    jni_policy_t policy;
    policy_java_to_c(env, policyObj, &policy);
    /*printf("sub_policy_num:%d\n", policy.sub_policy_num);
    for(int i = 0; i < policy.sub_policy_num; i++) {
        printf("id: %d position: %d length: %d\n", i, policy.subPolicy[i].position, policy.subPolicy[i].length);
    }*/

    jbyte *in;
    unsigned char ckey[CLEN];
    int inlen = 0;
    jbyteArray jout;

    in = (*env).GetByteArrayElements(instr, 0);

    inlen = (*env).GetArrayLength(instr);
    jbyte out[inlen];

    init_ckey(key, cptmode, DECRYPT_KEY, ckey);
    multi_sub_policy_ct_decrypt((unsigned char *) in, &policy, ckey, cptmode, (unsigned char *) out);

    jout = (*env).NewByteArray(inlen);
    (*env).SetByteArrayRegion(jout, 0, inlen, out);
    (*env).ReleaseByteArrayElements(instr, in, 0);

    return jout;
}
